<html lang="ja">
<meta charset="UTF-8" />
<head>
<title>bitmap font parse lines</title>
<script type="text/javascript">

// http://www.html5.jp/canvas/ref.html

function $(name) {
	return document.getElementById(name);
}

function getKeys(arr) {
	var keys = [];
	for (key in arr) {
		keys.push(key);
	}
	return keys;
}

function isEqual(a1, a2)
{
	if (a1 instanceof Array && a2 instanceof Array) {
		if (a1.length != a2.length) {
			return false;
		}
		for (var i=0; i<a1.length; ++i) {
			if (a1[i] != a2[i]) {
				return false;
			}
		}
		return true;
	}else {
		return false;
	}
}

function removeChildren(elem)
{
	while (elem.firstChild) {
		elem.removeChild(elem.firstChild);
	}
	return elem;
}

function arr2tbl(arr)
{
	if (!(arr instanceof Array)) {
		return;
	}
	if (arr.length == 0) {
		return;
	}
	for (var i=0; i<arr.length; ++i) {
		if (!(arr[i] instanceof Object)) {
			return;
		}
	}
	// header
	var keys = getKeys(arr[0]);
	for (var i=1; i<arr.length; ++i) {
		var keys2 = getKeys(arr[i]);
		if (!isEqual(keys, keys2)) {
			return;
		}
	}
	
	var tbl = document.createElement("table");
	
	// row
	for (var i=0; i<arr.length; ++i) {
		var row = tbl.insertRow(-1);
		var o = arr[i];
		row.data = o;
		for (var j=0; j<keys.length; ++j) {
			var c = row.insertCell(-1);
			c.innerHTML = o[keys[j]];
		}
	}
	
	// head
	var hdr = tbl.createTHead();
	var row = hdr.insertRow(0);
	for (var i=0; i<keys.length; ++i) {
		var c = document.createElement('th');
		row.appendChild(c);
		c.innerHTML = keys[i];
	}
	
	return tbl;
}

/*
var req = new XMLHttpRequest();
req.open('GET', 'file:///home/user/file.json', false); 
req.send(null);
if (req.status == 0)
  dump(req.responseText);
*/

var DEFAULT_SRC = "../shnm16.png";
var img = new Image();
var ctx;
var fontSize = 0;
var nCols;
var nRows;
var focusLine;

window.onload = function () {
	ctx = $("canvas").getContext("2d");
	ctx.mozImageSmoothingEnabled = false;
	$("defaultSrc").innerHTML = DEFAULT_SRC;
	img.src = DEFAULT_SRC;
}

img.onload = function () {
	$("imageInfo").innerHTML = ""
		+ "width : " + img.width + "<br>"
		+ "height : " + img.height + "<br>"
	;
	setFontSize(1);
};

function loadImage(file) {
	var fr = new FileReader();
	fr.onload = function () {
		img.src = fr.result;
	}
	fr.readAsDataURL(file);

}


function setFontSize() {
	fontSize = Number($("fontSize").value);
	nCols = img.width / fontSize;
	nRows = img.height / fontSize;
	$("fontInfo").innerHTML = ""
		+ "nCols : " + nCols + "<br>"
		+ "nRows : " + nRows
	;
	setCharCode();
}

function setCharCode(updateTable) {
	var code = $("charCode").value;
	var row = Math.floor(code / nCols);
	var col = code % nCols;
	
	processChar(row, col, updateTable);
}

function getBoxInfo(pixels, fontSize)
{
	var minX = fontSize;
	var minY = fontSize;
	var maxX = 0;
	var maxY = 0;
	for (var y=0; y<fontSize; ++y) {
		for (var x=0; x<fontSize; ++x) {
			if (pixels[y*fontSize+x]) {
				minX = Math.min(minX, x);
				minY = Math.min(minY, y);
				maxX = Math.max(maxX, x);
				maxY = Math.max(maxY, y);
			}
		}
	}
	return { x : minX, y : minY, w : maxX - minX + 1, h : maxY - minY + 1 };
}

function renderPixelsGrid(blockSize, ox, oy, pixels)
{
	var bw = pixels.w;
	var bh = pixels.h;
	var ox2 = ox + blockSize * 1;
	var oy2 = oy + blockSize * 1;0
	ctx.strokeRect(ox2-1, oy2-1, bw*blockSize+4, bh*blockSize+4);
	for (var y=0; y<bh; ++y) {
		for (var x=0; x<bw; ++x) {
			if (pixels[y*bw+x]) {
				ctx.fillStyle = "#000000";
			}else {
				ctx.fillStyle = "#FFFFFF";
			}
			ctx.fillRect(ox2+x*blockSize+2, oy2+y*blockSize+2, blockSize-2, blockSize-2);
			
			if (focusLine) {
				var bDraw = false;
				if (focusLine.dir == "h") {
					if (focusLine.y == y && x >= focusLine.x && x < focusLine.x+focusLine.len) {
						bDraw = true;
					}
				}else {
					if (focusLine.x == x && y >= focusLine.y && y < focusLine.y+focusLine.len) {
						bDraw = true;
					}
				}
				if (bDraw) {
					ctx.fillStyle = "#00FF00";
					ctx.fillRect(ox2+x*blockSize+2, oy2+y*blockSize+2, blockSize-2, blockSize-2);
				}
			}
		}
	}
	ctx.textBaseline = "top";
	ctx.font = (blockSize - 5).toString() + "px Arial";
	ctx.fillStyle = "darkblue";
	for (var i=0; i<bw; ++i) {
		var met = ctx.measureText(i).width;
		var x = ox2 + blockSize*i + (blockSize-met)/2;
		ctx.fillText(i, x, oy);
		ctx.fillText(i, x, oy2 + blockSize*(bh+0.5));
	}
	for (var i=0; i<bh; ++i) {
		var met = ctx.measureText(i).width;
		var x = ox + (blockSize-met)/2 - blockSize * 0.3;
		var y = oy2 + blockSize*i+4;
		ctx.fillText(i, x, y);
		ctx.fillText(i, x+blockSize*(bw+1.6), y);
	}
}

function compact(pixels, boxInfo)
{
	var arr = new Array(boxInfo.w * boxInfo.h);
	for (var y=0; y<boxInfo.h; ++y) {
		for (var x=0; x<boxInfo.w; ++x) {
			arr[y*boxInfo.w+x] = pixels[(boxInfo.y+y)*fontSize+boxInfo.x+x];
		}
	}
	return arr;
}

function getHorizontalRepeatLen(pixels, x, y)
{
	var i = 0;
	for (; i<pixels.w-x; ++i) {
		if (!pixels.at(x+i, y)) {
			break;
		}
	}
	return i;
}

function getVerticalRepeatLen(pixels, x, y)
{
	var i = 0;
	for (; i<pixels.h-y; ++i) {
		if (!pixels.at(x, y+i)) {
			break;
		}
	}
	return i;
}


function searchFills(pixels)
{
	var fills = {};
	
	var hFills = [];
	
	// 横方向の２ドット以上の連続塗りつぶしを調査
	for (var y=0; y<pixels.h; ++y) {
		for (var x=0; x<pixels.w; ++x) {
			var len = getHorizontalRepeatLen(pixels, x, y);
			if (len > 1) {
				hFills.push(
					{
						"dir" : "h",
						"x" : x,
						"y" : y,
						"len" : len
					}
				);
			}
			x += len;
		}
	}
	
	fills.horizontal_fills = hFills;
	
	var vFills = [];
	
	// 縦方向の２ドット以上の連続塗りつぶしを調査
	for (var x=0; x<pixels.w; ++x) {
		for (var y=0; y<pixels.h; ++y) {
			var len = getVerticalRepeatLen(pixels, x, y);
			if (len > 1) {
				vFills.push(
					{
						"dir" : "v",
						"x" : x,
						"y" : y,
						"len" : len,
					}
				);
			}
			y += len;
		}
	}
	
	fills.vertical_fills = vFills;
	
	// 単独1ドットの塗りつぶしを調査（縦方向にする）
	for (var x=0; x<pixels.w; ++x) {
		for (var y=0; y<pixels.h; ++y) {
			if (!pixels.at(x,y)) {
				continue;
			}
			var left = false;
			var right = false;
			var top = false;
			var bottom = false;
			if (x == 0) {
				right = pixels.at(x+1,y);
			}else if (x == pixels.w-1) {
				left = pixels.at(x-1,y);
			}else {
				right = pixels.at(x+1,y);
				left = pixels.at(x-1,y);
			}
			if (y == 0) {
				bottom = pixels.at(x,y+1);
			}else if (y == pixels.h-1) {
				top = pixels.at(x,y-1);
			}else {
				bottom = pixels.at(x,y+1);
				top = pixels.at(x,y-1);
			}
			if (left || right || top || bottom) {
				continue;
			}else {
				vFills.push(
					{
						"dir" : "v",
						"x" : x,
						"y" : y,
						"len" : 1
					}
				);
			}
		}
	}
	
	// 縦線の終端から引ける斜め線を探す。
	
	
	return fills;
}

function setOnMouseOver(tbl)
{
	var className = "hover";
	var rows = tbl.getElementsByTagName("tr");
	for (var i=0; i<rows.length; ++i) {
		var row = rows[i];
		row.baseName = row.className;
		row.onmouseover = function() { this.className = className; focusLine = this.data; setTimeout("setCharCode(0);", 10); }
		row.onmouseout = function() { this.className = this.baseName; }
	}
}

function processChar(row, col, updateTable)
{
	ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height)
	ctx.drawImage(
		img,
		col*fontSize, row*fontSize, fontSize, fontSize,
		0,0,fontSize, fontSize
		);
	var data = ctx.getImageData(0, 0, fontSize, fontSize);
	pixels = new Array(fontSize*fontSize);
	for (var y=0; y<fontSize; ++y) {
		for (var x=0; x<fontSize; ++x) {
			pixels[y*fontSize+x] = !data.data[(fontSize*y+x)*4];
		}
	}
	
	var boxInfo = getBoxInfo(pixels, fontSize);
	$("boxInfo").innerHTML = "box info<br>"
		+ "<div style='padding-left: 1.0em;'>"
		+ "x : " + boxInfo.x + "<br>"
		+ "y : " + boxInfo.y + "<br>"
		+ "w : " + boxInfo.w + "<br>"
		+ "h : " + boxInfo.h + "<br>"
		+ "</div>"
	;
	
	pixels = compact(pixels, boxInfo);
	pixels.w = boxInfo.w;
	pixels.h = boxInfo.h;
	pixels.at = function (x, y) {
		return this[y*this.w+x];
	}
	fills = searchFills(pixels);
	renderPixelsGrid(20, 10, 10, pixels);
	
	if (updateTable) {
		var ctn, tbl;
		
		ctn = $("horizontal_fills");
		removeChildren(ctn);
		tbl = arr2tbl(fills.horizontal_fills);
		setOnMouseOver(tbl);
		ctn.appendChild(tbl);
		
		ctn = $("vertical_fills");
		removeChildren(ctn);
		tbl = arr2tbl(fills.vertical_fills);
		setOnMouseOver(tbl);
		ctn.appendChild(tbl);
	}
}

</script>
<style type="text/css">
<!--
input { ime-mode: disabled; }

table {
    border: 1px #E3E3E3 solid;
    border-collapse: collapse;
    border-spacing: 0;
}

table th {
    padding: 5px;
    border: #E3E3E3 solid;
    border-width: 0 0 1px 1px;
    background: #F5F5F5;
    font-weight: bold;
    line-height: 120%;
    text-align: center;
}
table td {
    padding: 5px;
    border: 1px #E3E3E3 solid;
    border-width: 0 0 1px 1px;
    text-align: center;
}

tr.hover {
    background-color: #cccccc;
}


-->
</style>
</head>
<body style="background-color:#aaa; line-height:150%;">

default src : <span id="defaultSrc"></span><br>
load file : <input type="file" size="80" onchange="loadImage(this.files[0]);" /><br>
<span id="imageInfo"></span>
font size : <input id="fontSize" type="number" value="16" min="0" max="32" onkeydown="if (event.which==13) setFontSize();" /><input type="button" value="set" onclick="setFontSize();" /><br>
<span id="fontInfo"></span><br>
char code : <input id="charCode" type="number" default="150" min="0" onkeydown="if (event.which==13) setCharCode(1);" /><input type="button" value="set" onclick="setCharCode(1);" /><br>
<span id="boxInfo"></span><br>

<div style="position:relative; top:0px; left:0px;">

<canvas id="canvas" style="position:relative; top:0px; left:0px; border-style:solid; border-width:1px;"width="512" height="512"></canvas>

<div id="horizontal_fills" style="position:absolute; top:0px; left:532px;"></div>
<div id="vertical_fills" style="position:absolute; top:0px; left:670px;"></div>


</div>

</body>
</html>
